#include "w25qxx.h"
#include <string.h>
#include "cbb_delay.h"
#include "device.h"
#include <stdio.h>


// W25QXX CMD
#define W25QXX_CMD_WRITE_ENABLE     0x06
#define W25QXX_CMD_READ_STATUS_REG1 0x05
#define W25QXX_CMD_READ_DATA        0x03
#define W25QXX_CMD_PAGE_PROGRAM     0x02
#define W25QXX_CMD_SECTOR_ERASE     0x20
// #define W25QXX_CMD_BLOCK32_ERASE    0x52
// #define W25QXX_CMD_BLOCK64_ERASE    0xD8
#define W25QXX_CMD_CHIP_ERASE       0xC7
#define W25QXX_CMD_READ_CHIP_ID     0x90 

// CS宏定义
#define CS_DISABLE()    pstW25QXX->HW.pfnCS(!pstW25QXX->SW.u8CSActive)   // CS HIGH
#define CS_ENABLE()     pstW25QXX->HW.pfnCS(pstW25QXX->SW.u8CSActive)    // CS LOW

DEV_REG_DEF(w25qxx, W25QXX_t)

/**
 * @brief 等待W25QXX忙
 * @param pstW25QXX W25QXX设备
 * @param u32Delay 延时时间
 * @return @see DEV_RET_t
 */
static DEV_RET_t
w25qxx_wait_busy_impl(W25QXX_t* pstW25QXX){

    // 超时时间
    uint32_t u32TimeOut = pstW25QXX->SW.u32RetryNum;

    // 待发送的命令
    uint8_t u8TxData = W25QXX_CMD_READ_STATUS_REG1;

    // 待接收的缓存
    uint8_t u8RxData = 0x00;

    while(1){

        // 发送命令获取状态
        CS_ENABLE();
        if(pstW25QXX->HW.pfnTransRecv(&u8TxData, &u8RxData, 1) != 0) return DEV_ERR;
        CS_DISABLE();

        // 等待busy标志
        if(u8RxData & 0x01) delay_ms(3);
        else break;

        // 超时
        if(--u32TimeOut == 0) return DEV_TIMEOUT;
    }

    return DEV_OK;
}

static DEV_RET_t
w25qxx_write_enable_impl(W25QXX_t* pstW25QXX){

    // 超时时间
    uint32_t u32TimeOut = pstW25QXX->SW.u32RetryNum;

    // 待发送的命令
    uint8_t u8TxData[2] = {W25QXX_CMD_WRITE_ENABLE, W25QXX_CMD_READ_STATUS_REG1};

    // 待接收的缓存
    uint8_t u8RxData;

    while(1){

        // 发送命令使能写操作以及获取状态
        CS_ENABLE();
        if(pstW25QXX->HW.pfnTransRecv(&u8TxData[0], NULL, 1) != 0) return DEV_ERR;
        CS_DISABLE();

        CS_ENABLE();
        if(pstW25QXX->HW.pfnTransRecv(&u8TxData[1], &u8RxData, 1) != 0) return DEV_ERR;
        CS_DISABLE();

        // 等待wel标志 -- 写使能成功
        if(!(u8RxData & 0x02) || u8RxData == 0xFF) delay_ms(3);
        else break;
        

        // 超时
        if(--u32TimeOut == 0) return DEV_TIMEOUT;
    }

    // printf("status reg1: %x\n", u8RxData);
    return DEV_OK;
}

static DEV_RET_t w25qxx_read_data_impl(W25QXX_t* pstW25QXX, uint8_t* pu8RxData, uint32_t u32Addr, uint32_t u32Len)
{
    // 待发送的命令
    uint8_t u8TxData[4] = {
        W25QXX_CMD_READ_DATA,
        (uint8_t)((u32Addr >> 16) & 0xFF), 
        (uint8_t)((u32Addr >> 8) & 0xFF),
        (uint8_t)(u32Addr & 0xFF)        
    };

    // 等待busy -- 防止指令被忽略
    uint8_t u8Ret = w25qxx_wait_busy_impl(pstW25QXX);
    if(u8Ret != DEV_OK) return u8Ret;

    // 发送命令获取数据
    CS_ENABLE();
    if(pstW25QXX->HW.pfnTransRecv(u8TxData, NULL, 4) != 0) return DEV_ERR;
    if(pstW25QXX->HW.pfnTransRecv(NULL, pu8RxData, u32Len) != 0) return DEV_ERR;
    CS_DISABLE();

    // 等待读取完成
    delay_us(800);
    return w25qxx_wait_busy_impl(pstW25QXX);
}

static DEV_RET_t 
w25qxx_clear_sector_impl(W25QXX_t* pstW25QXX, uint32_t u32Addr){

    uint8_t u8Ret;

    // 待发送的命令 -- 强制对其扇区
    u32Addr = u32Addr * SECTOR_SIZE;
    uint8_t u8TxData[4] = {W25QXX_CMD_SECTOR_ERASE, 
                        (u32Addr >> 16) & 0xFF, 
                        (u32Addr >> 8) & 0xFF, 
                        u32Addr&0xff
    };
    
    // 等待空闲
    u8Ret = w25qxx_wait_busy_impl(pstW25QXX);
    if(u8Ret != DEV_OK) return u8Ret;

    // 使能写操作
    u8Ret = w25qxx_write_enable_impl(pstW25QXX);
    if(u8Ret != DEV_OK) return u8Ret;

    // 等待空闲
    u8Ret = w25qxx_wait_busy_impl(pstW25QXX);
    if(u8Ret != DEV_OK) return u8Ret;

    // 发送命令擦除扇区
    CS_ENABLE();
    if(pstW25QXX->HW.pfnTransRecv(u8TxData,NULL,4) != 0) return DEV_ERR;
    CS_DISABLE();

    // 等待擦除完成
    delay_us(1000);
    return w25qxx_wait_busy_impl(pstW25QXX);
}

static DEV_RET_t 
w25qxx_write_sector_impl(W25QXX_t* pstW25QXX, uint8_t* pu8TxData, uint32_t u32Addr)
{
    // 返回值
    uint8_t u8Ret;

    // 扇区起始地址
    u32Addr = u32Addr * SECTOR_SIZE;

    // 清空扇区
    u8Ret = w25qxx_clear_sector_impl(pstW25QXX, u32Addr / SECTOR_SIZE);
    if(u8Ret != DEV_OK) return u8Ret;

    // 扇区:4096bytes, 缓存页:256bytes, 写扇区要分16次页命令写入
    for (char i = 0; i < 16; i++)
    {
        // 准备写指令和地址
        uint8_t u8Header[4] = {
            W25QXX_CMD_PAGE_PROGRAM,
            (uint8_t)((u32Addr >> 16) & 0xFF),
            (uint8_t)((u32Addr >> 8) & 0xFF),
            (uint8_t)(u32Addr & 0xFF)
        };

        // 等待空闲
        u8Ret = w25qxx_wait_busy_impl(pstW25QXX);
        if(u8Ret != DEV_OK) return u8Ret;

        // 写使能
        u8Ret = w25qxx_write_enable_impl(pstW25QXX);
        if(u8Ret != DEV_OK) return u8Ret;

        // 等待空闲
        u8Ret = w25qxx_wait_busy_impl(pstW25QXX);
        if(u8Ret != DEV_OK) return u8Ret;

        // 发送写指令和地址以及数据
        CS_ENABLE();
        if(pstW25QXX->HW.pfnTransRecv(u8Header, NULL, 4) != 0) return DEV_ERR;
        if(pstW25QXX->HW.pfnTransRecv(pu8TxData + PAGE_SIZE*i, NULL, PAGE_SIZE) != 0) return DEV_ERR;
        CS_DISABLE();

        // 等待写入完成
        delay_us(800);
        u8Ret = w25qxx_wait_busy_impl(pstW25QXX);
        if(u8Ret != DEV_OK) return u8Ret;

        pu8TxData += PAGE_SIZE; // 缓存指针增加一页字节数
        u32Addr += PAGE_SIZE ;  // 写地址增加一页字节数
    }

    return DEV_OK;
}

DEV_RET_t
w25qxx_read_id(Device_t* pstDevice, uint16_t* pu16ID){

    // 输入参数检查
    if(NULL == pstDevice || NULL == pu16ID || NULL == pstDevice->pvFeature) return DEV_PARAM_ERR;

    // 设备检测
    CHECK_DEVICE(pstDevice);

    // 转换为W25QXX结构体
    W25QXX_t* pstW25QXX = (W25QXX_t*)pstDevice->pvFeature;

    // 输入参数检查
    if(NULL == pstW25QXX->HW.pfnTransRecv || NULL == pstW25QXX->HW.pfnCS) {DEV_RETURN(DEV_PARAM_ERR)};

    // 待发送的命令
    uint8_t u8TxData[6] = {W25QXX_CMD_READ_CHIP_ID, 0x00, 0x00, 0x00,0x00,0x00};

    // 接收缓存
    uint8_t u8RxData[6] = {0x00};
    
    // 发送命令获取ID
    CS_ENABLE();
    if(pstW25QXX->HW.pfnTransRecv(u8TxData, u8RxData, 6) != 0) {DEV_RETURN(DEV_TIMEOUT)};
    CS_DISABLE();

    // 解析ID
    *pu16ID = (uint16_t)u8RxData[4] << 8 | (uint16_t)u8RxData[5];

    // 返回成功
    DEV_RETURN(DEV_OK);
}

DEV_RET_t
w25qxx_read_data(Device_t* pstDevice, uint8_t* pu8RxData, uint32_t u32Addr, uint32_t u32Len){
    
    // 输入参数检查
    if(NULL == pstDevice || NULL == pu8RxData || NULL == pstDevice->pvFeature) return DEV_PARAM_ERR;

    // 设备检测
    CHECK_DEVICE(pstDevice);

    // 转换为W25QXX结构体
    W25QXX_t* pstW25QXX = (W25QXX_t*)pstDevice->pvFeature;

    // 输入参数检查
    if(NULL == pstW25QXX->HW.pfnTransRecv || NULL == pstW25QXX->HW.pfnCS) {DEV_RETURN(DEV_PARAM_ERR)};

    // 读取数据
    uint8_t u8Ret = w25qxx_read_data_impl(pstW25QXX, pu8RxData, u32Addr, u32Len);

    // 返回结果
    DEV_RETURN(u8Ret)
}

DEV_RET_t 
w25qxx_clear_sector(Device_t* pstDevice, uint32_t u32Addr){

    // 输入参数检查
    if(NULL == pstDevice || NULL == pstDevice->pvFeature) return DEV_PARAM_ERR;

    // 设备检测
    CHECK_DEVICE(pstDevice);

    // 转换为W25QXX结构体
    W25QXX_t* pstW25QXX = (W25QXX_t*)pstDevice->pvFeature;

    // 输入参数检查
    if(NULL == pstW25QXX->HW.pfnTransRecv || NULL == pstW25QXX->HW.pfnCS) {DEV_RETURN(DEV_PARAM_ERR)};

    // 擦除扇区
    uint8_t u8Ret = w25qxx_clear_sector_impl(pstW25QXX, u32Addr);

    // 返回结果
    DEV_RETURN(u8Ret);
}

// 5_5 写扇区. 要分页写入
DEV_RET_t 
w25qxx_write_sector(Device_t* pstDevice, uint8_t* pu8TxData, uint32_t u32Addr)
{
    // 输入参数检查
    if(NULL == pstDevice || NULL == pstDevice->pvFeature) return DEV_PARAM_ERR;

    // 设备检测
    CHECK_DEVICE(pstDevice);

    // 转换为W25QXX结构体
    W25QXX_t* pstW25QXX = (W25QXX_t*)pstDevice->pvFeature;

    // 输入参数检查
    if(NULL == pstW25QXX->HW.pfnTransRecv || NULL == pstW25QXX->HW.pfnCS) {DEV_RETURN(DEV_PARAM_ERR)};

    // 返回值
    uint8_t u8Ret = w25qxx_write_sector_impl(pstW25QXX, pu8TxData, u32Addr);

    // 返回结果
    DEV_RETURN(u8Ret);
}
                  

DEV_RET_t w25qxx_write_data(Device_t* pstDevice, uint8_t* pu8TxData, uint32_t u32Addr, uint32_t u32Len) {

    // 输入参数检查
    if(NULL == pstDevice || NULL == pu8TxData || NULL == pstDevice->pvFeature) return DEV_PARAM_ERR;

    // 设备检测
    CHECK_DEVICE(pstDevice);

    // 转换为W25QXX结构体
    W25QXX_t* pstW25QXX = (W25QXX_t*)pstDevice->pvFeature;

    // 输入参数检查
    if(NULL == pstW25QXX->HW.pfnTransRecv || NULL == pstW25QXX->HW.pfnCS) {DEV_RETURN(DEV_PARAM_ERR)};

    // 返回值
    uint8_t u8Ret; 

    // 等待空闲
    u8Ret = w25qxx_wait_busy_impl(pstW25QXX);
    if(u8Ret != DEV_OK) {DEV_RETURN(u8Ret)};

    uint32_t  u32SecPos      = u32Addr / 4096;      // 扇区起始地址
    uint16_t  u32SecOff      = u32Addr % 4096;      // 起始扇区内偏移地址
    uint16_t  u16SecRemain   = 4096 - u32SecOff;    // 扇区剩余空间字节数
    uint8_t  *pu8Buffer      = pstW25QXX->SW.pu8SecCache;        

    // 判断长度是否大于一个扇区
    if (u32Len <= u16SecRemain) u16SecRemain = u32Len;

    // 循环写入
    while (1)
    {
        // 读取扇区内原始数据到缓存区
        u8Ret = w25qxx_read_data_impl(pstW25QXX, pu8Buffer, u32SecPos * SECTOR_SIZE, SECTOR_SIZE);
        if(u8Ret != DEV_OK) {DEV_RETURN(u8Ret)};

        // 写入数据到缓存区
        memcpy(pu8Buffer + u32SecOff, pu8TxData, u16SecRemain);

        // 写入扇区
        u8Ret = w25qxx_write_sector_impl(pstW25QXX,pu8Buffer, u32SecPos);
        if(u8Ret != DEV_OK) {DEV_RETURN(u8Ret)};

        if (u16SecRemain == u32Len) break; // 写完了
        else
        {
            // 未写完
            pu8TxData = pu8TxData + u16SecRemain ;              // 原始数据指针偏移
            u32SecPos ++;                                       // 新扇区
            u32SecOff = 0;                                      // 新偏移位,扇区内数据起始地址
            u32Len = u32Len - u16SecRemain ;                    // 剩余未写字节数
            u16SecRemain = (u32Len > 4096) ? 4096 : u32Len;     // 计算新扇区写入字节数
        }
    }

    DEV_RETURN(DEV_OK);
}

// DEV_RET_t 
// w25qxx_clear_chip(Device_t* pstDevice){

    
//     // W25QXX_t* pstW25QXX = (W25QXX_t*)pstDevice->pvFeature;

//     // // wait busy
//     // W25QXX_WaiteBusy(pstDevice, 200);

//     // // write enable
//     // W25QXX_WriteEnable(pstDevice);
//     // W25QXX_WaiteBusy(pstDevice,200);

//     // uint8_t u8TxData[1] = {W25QXX_CMD_CHIP_ERASE};

//     // // send cmd to erase sector
//     // CS_ENABLE();
//     // pstW25QXX->HW.pfnTransRecv(u8TxData,NULL,1);
//     // CS_DISABLE();

//     // // wait busy
//     // return W25QXX_WaiteBusy(pstDevice, 200000);

//     // return DEV_OK;
// }

#if 0
DEV_RET_t 
w25qxx_clear_block32(Device_t* pstDevice, uint32_t u32Addr){

    
    W25QXX_t* pstW25QXX = (W25QXX_t*)pstDevice->pvFeature;

    // wait busy
    W25QXX_WaiteBusy(pstDevice, 200);

    // write enable
    W25QXX_WriteEnable(pstDevice);
    W25QXX_WaiteBusy(pstDevice,200);

    uint8_t u8TxData[4] = {W25QXX_CMD_BLOCK32_ERASE, 
        (u32Addr >> 16) & 0xFF, 
        (u32Addr >> 8) & 0xFF, 
        u32Addr&0xff
    };


    // send cmd to erase sector
    CS_ENABLE();
    pstW25QXX->HW.pfnTransRecv(u8TxData,NULL,4);
    CS_DISABLE();

    // wait busy
    return W25QXX_WaiteBusy(pstDevice, 7500);


    return DEV_OK;
}

DEV_RET_t 
w25qxx_clear_block64(Device_t* pstDevice, uint32_t u32Addr){

    
    W25QXX_t* pstW25QXX = (W25QXX_t*)pstDevice->pvFeature;

    // wait busy
    W25QXX_WaiteBusy(pstDevice, 200);

    // write enable
    W25QXX_WriteEnable(pstDevice);
    W25QXX_WaiteBusy(pstDevice,200);

    uint8_t u8TxData[4] = {W25QXX_CMD_BLOCK64_ERASE, 
        (u32Addr >> 16) & 0xFF, 
        (u32Addr >> 8) & 0xFF, 
        u32Addr&0xff
    };

    // send cmd to erase sector
    CS_ENABLE();
    pstW25QXX->HW.pfnTransRecv(u8TxData,NULL,10000);
    CS_DISABLE();

    // wait busy
    return W25QXX_WaiteBusy(pstDevice, 5000);

    return DEV_OK;
}
#endif

